package liquidfs.ui.cmd;

import java.io.*;
import java.util.*;

import utility.*;

import liquidfs.*;
import liquidfs.core.*;
import liquidfs.data.*;
import liquidfs.data.ex.*;
import liquidfs.data.fs.*;

public class CommandLine implements Runnable {

   private String workingDir = "/";
   private List<CommandHandler> handlers = new ArrayList<CommandHandler> ();
   
   private Application app;
   private LiquidFileSystem fs;
  
   
   public CommandLine (Application app) {
      
      this.app = app;
      this.fs = app.getFileSystem();
      
      // Add the handlers to the list
      handlers.add(linksHandler);
      handlers.add(lsHandler);
      handlers.add(cdHandler);
      handlers.add(catHandler);
      handlers.add(writeHandler);
   }
   
   public void run () {
      
      // Reader for typed input
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      
      while (true) {
         
         // Print the command line
         System.out.print(workingDir + " $ ");
         
         String cmd;
         
         // Read in the command
         try {
            cmd = in.readLine();
         }
         catch (IOException e) {
            // Skip this command if there was an error reading it
            continue;
         }
         
         // If the command is all whitespace, skip it.
         if (cmd.trim().length() == 0)
            continue;
         
         // Break the command up into its components
         String[] components = cmd.split(" ");
         
         // Replace built-in variables
         for (int i = 0; i < components.length; ++i) {
            if (components[i].equals(".")) {
               components[i] = this.workingDir;
            }
            else if (components[i].equals("..")) {
               
               // Cut off the last component of the path
               components[i] = this.workingDir.substring(0, workingDir.lastIndexOf('/'));
               
               // Correct the root directory's name
               if (components[i].isEmpty())
                  components[i] = "/";
            }
         }
         
         boolean handled = false;
         
         // Handle the command
         for (CommandHandler handler : this.handlers) {
            if (handler.canHandle(components[0])) {
               handler.handle(components);
               handled = true;
               break;
            }
         }
         
         // Warn the user if we didn't understand
         if (!handled) {
            System.out.println("Could not find a handler for that command.");
         }
                  
      }
   }
   
   public String getAbsolutePath (String path) {
      if (path.startsWith("/")) {
         return path;
      }
      else {
         if (workingDir.equals("/")) {
            return workingDir + path;
         }
         else {
            return workingDir + "/" + path;
         }
      }
   }
   
   private CommandHandler catHandler = new CommandAdapter ("cat") {
      public void handle (String... arguments) {
         
         if (arguments.length < 2) {
            System.out.println("No file specified.");
         }
         
         String path = getAbsolutePath(arguments[1]);
         
         LiquidObject object = fs.getPath(path);
         LiquidFile file;
         
         try {
            file = object.asFile();
         }
         catch (WrongObjectTypeException e) {
            System.out.println("That is a directory, not a file.");
            return;
         }
         
         try {
            
            // Open the file
            file.open(OpenMode.READ);
            
            // Get the input stream
            InputStreamReader reader = new InputStreamReader(file.getInputStream());
            
            // Only read the first ten lines or so
            char[] data = new char[10 * 80];
            int read;
            
            try {
               read = reader.read(data);
            }
            catch (IOException e) {
               throw new FileSystemException(e.getLocalizedMessage());
            }
            
            file.close();
            
            String string = new String(data, 0, read);
            System.out.println(string);
         }
         catch (FileSystemException e) {
            System.out.println(e.getLocalizedMessage());
            return;
         }
      }
   };
   
   private CommandHandler linksHandler = new CommandAdapter("links") {
      public void handle (String... arguments) {
         
         // Get the links that we have made
         List<Link> links = fs.getConfiguration().getLinks();
         
         // Blank a line for clarity
         System.out.println();
         
         // Show each link
         for (Link link : links) {
            System.out.printf("%-30s %-12s %s\n", link.getHost().toString(), link.getDevice().getID(), link.getDevice().getName());
         }
         
         // Blank a line for clarity
         System.out.println();
      }
   };
   
   private CommandHandler cdHandler = new CommandAdapter("cd") {
      public void handle (String... arguments) {
         
         // No dir given
         if (arguments.length < 2) {
            workingDir = "/";
         }
         else {
            workingDir = getAbsolutePath(arguments[1]);
         }
         
      }
   };
   
   private CommandHandler lsHandler = new CommandAdapter("ls") {
      public void handle(String... arguments) {
         
         // By default, use the working directory if no path was given
         String path = workingDir;
         
         // If the user specified a path
         if (arguments.length >= 2) {
            path = getAbsolutePath(arguments[1]);
         }
         
         // Get the object at that path
         LiquidObject object = fs.getPath(path);
         LiquidDirectory dir;
         
         // Make sure it's a directory
         try {
            dir = object.asDirectory();
         }
         catch (WrongObjectTypeException e) {
            System.out.println("Not a directory.");
            return;
         }
         
         // Get the contents of that directory
         List<LiquidObject> contents = dir.getContents();
         
         // Blank line for clarity
         System.out.println();
         
         // Print them out onto the screen
         for (LiquidObject item : contents) {
            if (item.isDirectory()) {
               System.out.printf("%-35s %s (%d)\n", item.getPath(), fs.isLocal(item.getPath()) ? " " : "R", item.getStorageDevices().size());
            }
            else {
               System.out.printf("%-35s %s (%d) %4db\n", item.getPath(), fs.isLocal(item.getPath()) ? " " : "R", item.getStorageDevices().size(), item.getSize());
            }
         }
         
         // Blank line for clarity
         System.out.println();
         
      }
   };
   
   private CommandHandler writeHandler = new CommandAdapter("write") {
      public void handle (String... arguments) {
         
         if (arguments.length < 2) {
            System.out.println("No file specified.");
         }
         
         String path = getAbsolutePath(arguments[1]);
         
         LiquidObject object = fs.getPath(path);
         LiquidFile file;
         
         try {
            file = object.asFile();
         }
         catch (WrongObjectTypeException e) {
            System.out.println("That is a directory, not a file.");
            return;
         }
         
         try {
            
            // Open the file
            file.open(OpenMode.READ_WRITE);
            
            // Truncate the file
            file.truncate();
            
            // Get the input stream
            //OutputStreamWriter writer = new OutputStreamWriter(file.getOutputStream());
            OutputStream binWriter = file.getOutputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            
            try {
               String line = reader.readLine();
               //writer.write(line);
               binWriter.write(line.getBytes());
            }
            catch (IOException e) {
               throw new FileSystemException(e.getLocalizedMessage());
            }
            
            file.close();
         }
         catch (FileSystemException e) {
            System.out.println("Unable to write file: " + e.getLocalizedMessage());
            return;
         }
         
      }
   };
}
